package com.lagou.loadbalance;

import com.lagou.register.ZkUtil;
import com.lagou.rpc.*;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;

import java.net.InetSocketAddress;
import java.util.List;

public class DefaultLoadBalancer implements ILoadBalancer, PathChildrenCacheListener {

    public static CuratorFramework zkClient;
    public static ClientConnectionPool pool;
    private IRule rule;

    static {
        // 初始化, 获取所有服务可用列表
        init();
    }

    public static ClientConnectionPool getPool() {
        return pool;
    }

    public static CuratorFramework getZkClient() {
        return zkClient;
    }

    private static void init() {
        // 1.zk客户端(可从配置读取zk地址)
        zkClient = ZkUtil.getZkClient();
        // 2.获取server列表
        List<String> serviceList = ZkUtil.getServiceList(zkClient);
        // 3.初始化连接
        pool = new ClientConnectionPool(serviceList, zkClient);
    }

    private DefaultLoadBalancer() {
        // 1.创建默认负载均衡规则
        rule = new AvailabilityFilteringRule();
        // 2.监听服务变化 (即监听子节点列表)
        doWatch();
    }

    private static class SingletonHolder{
        private static DefaultLoadBalancer instance = new DefaultLoadBalancer();
    }

    public synchronized static DefaultLoadBalancer getInstance(){
        return SingletonHolder.instance;
    }

    private void doWatch(){
        /***
         * 节点缓存的监听: 有三种 (原生Watcher 和 CuratorWatcher都只能监听一次)
         * 1. NodeCache:  ZNode自身节点的监听
         * 2. PathChildrenCache:  用于ZNode的子节点列表的监听(增/删/更新/子节点内容变化)
         * 3. TreeCache: 不光能监听子节点, 也能监听ZNode节点自身
         */
        PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, "/",true);
        pathChildrenCache.getListenable().addListener(this);
        try {
            pathChildrenCache.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
        String service = event.getData().getPath();
        String[] arr = service.split(":");

        switch (event.getType()){
            case CHILD_ADDED:
                // ZNode添加, 添加服务, 并主动建立连接池
                System.out.println("扩容服务, 客户端收到通知, 主动建立一个连接池" + service);

                pool.connect(InetSocketAddress.createUnresolved(arr[0], Integer.parseInt(arr[1])));
                break;

            case CHILD_UPDATED:
                System.out.println("子节点数据变化:" + event.getData().getPath());
                break;

            case CHILD_REMOVED:
                // ZNode删除, 删除服务, 释放连接池
                System.out.println("服务端下线, 客户端收到通知, 客户端移除删除服务(就是连接池移除):" + service);
                pool.disconnect(InetSocketAddress.createUnresolved(arr[0], Integer.parseInt(arr[1])));
                break;
            default:
                break;
        }
    }

    @Override
    public InetSocketAddress chooseServer() {
        String choose = rule.choose(zkClient);
        String[] arr = choose.split(":");
        InetSocketAddress address = InetSocketAddress.createUnresolved(arr[0], Integer.parseInt(arr[1]));
        return address;
    }
}
